package websocket

import (
	"log"
	"sync"
)

// Hub maintains the set of active clients and broadcasts messages to the
// clients.
type Hub struct {
	// Registered clients.
	clients map[*Client]bool

	// Inbound messages from the clients.
	broadcast chan []byte

	// Register requests from the clients.
	register chan *Client

	// Unregister requests from clients.
	unregister chan *Client
	cnt        int
	mutex      sync.Mutex
}

func newHub() *Hub {
	return &Hub{
		broadcast:  make(chan []byte),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}
}

//这个是单线程执行的，所以 map 不需要上锁

func (h *Hub) run() {
	h.mutex.Lock()
	h.cnt++
	if h.cnt > 1 {
		//说明重入了
		log.Println("[warning]:h.Hub 只能单线程执行，如果要多线程，请保证 map 是线程安全的")
		h.cnt--
		h.mutex.Unlock()

		return
	}
	h.mutex.Unlock()
	for {
		select {
		case client := <-h.register:
			//收到连接消息
			h.clients[client] = true
		case client := <-h.unregister:
			//关闭连接
			if _, ok := h.clients[client]; ok {
				//第二个参数表示如果 map里面还有这个客户端，就删除这个客户端
				delete(h.clients, client)
				//关闭 客户端的 channel,这样就可以 将 等待发生 广播消息的那个程序断开
				close(client.send)
			}
		case message := <-h.broadcast:
			for client := range h.clients {
				select {

				case client.send <- message:
				default:
					//golang 也没有直接提供判断 channel 是否已经关闭的接口，虽然可以用其他不太优雅的方式自己实现一个：
					//select 必须要有 default 分支，否则会阻塞函数，我们这个函数要保证一定能正常返回；
					//一般不会走到这个分支的
					close(client.send)
					delete(h.clients, client)
					/*
						永远不要尝试在读取端关闭 channel ，写入端无法知道 channel 是否已经关闭，往已关闭的 channel 写数据会 panic ；
					*/
				}
			}
		}
	}
}
